package com.xu.sso.client.utils;

import com.alibaba.fastjson.JSONObject;
import io.lettuce.core.RedisURI;
import io.lettuce.core.ScriptOutputType;
import io.lettuce.core.SetArgs;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Component
public class ClientRedisClusterUtil implements DisposableBean {

    private final Logger log = LoggerFactory.getLogger(ClientRedisClusterUtil.class);

    @Value("${com.xu.sso.redis.host}")
    private String host;
    private RedisClusterClient redisClient = null;
    private StatefulRedisClusterConnection<String, String> connect = null;

    private RedisClusterClient getInstance() {
        if (null == redisClient) {
            synchronized (ClientRedisClusterUtil.class) {
                if (null == redisClient) {
                    List<RedisURI> list = new ArrayList<>();
                    if (host.contains(",")) {
                        String[] hosts = host.split(",");
                        Arrays.stream(hosts).forEach(h -> list.add(RedisURI.create("redis://" + h)));
                    } else {
                        list.add(RedisURI.create("redis://" + host));
                    }
                    redisClient = RedisClusterClient.create(list);
                    log.info(">>>>>>>>>>>>>>>>>>>>>>redis init success...");
                }
            }
        }
        return redisClient;
    }

    public StatefulRedisClusterConnection<String, String> getConnect() {
        if (null == connect || !connect.isOpen()) {
            synchronized (ClientRedisClusterUtil.class) {
                if (null == connect || !connect.isOpen()) {
                    connect = getInstance().connect();
                }
            }
        }
        return connect;
    }

    public void close(StatefulRedisClusterConnection<String, String> connect) {
        if (null != connect && connect.isOpen()) {
            connect.close();
        }
    }

    public boolean set(String key, String value) {
        try {
            RedisAdvancedClusterCommands<String, String> commands = getConnect().sync();
            String result = commands.set(key, value);
            if ("OK".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis set error === {}", e);
        }
        return false;
    }

    public boolean set(String key, String value, long ex) {
        try {
            RedisAdvancedClusterCommands<String, String> commands = getConnect().sync();
            String result = commands.setex(key, ex, value);
            if ("OK".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis set error === {}", e);
        }
        return false;
    }

    public boolean setnx(String key, String value) {
        try {
            RedisAdvancedClusterCommands<String, String> commands = getConnect().sync();
            String result = commands.set(key, value, SetArgs.Builder.nx());
            if ("OK".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis setnx error === {}", e);
        }
        return false;
    }

    public boolean setnx(String key, String value, long ex) {
        try {
            RedisAdvancedClusterCommands<String, String> commands = getConnect().sync();
            String result = commands.set(key, value, SetArgs.Builder.nx().ex(ex));
            if ("OK".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis setnx error === {}", e);
        }
        return false;
    }

    public boolean set(String key, Object obj) {
        try {
            RedisAdvancedClusterCommands<String, String> commands = getConnect().sync();
            String result = commands.set(key, JSONObject.toJSONString(obj));
            if ("OK".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis set error === {}", e);
        }
        return false;
    }

    public boolean set(String key, Object obj, long ex) {
        try {
            RedisAdvancedClusterCommands<String, String> commands = getConnect().sync();
            String result = commands.setex(key, ex, JSONObject.toJSONString(obj));
            if ("OK".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis set error === {}", e);
        }
        return false;
    }

    public boolean setnx(String key, Object obj) {
        try {
            RedisAdvancedClusterCommands<String, String> commands = getConnect().sync();
            String result = commands.set(key, JSONObject.toJSONString(obj), SetArgs.Builder.nx());
            if ("OK".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis setnx error === {}", e);
        }
        return false;
    }

    public boolean setnx(String key, Object obj, long ex) {
        try {
            RedisAdvancedClusterCommands<String, String> commands = getConnect().sync();
            String result = commands.set(key, JSONObject.toJSONString(obj), SetArgs.Builder.nx().ex(ex));
            if ("OK".equals(result)) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis setnx error === {}", e);
        }
        return false;
    }

    public Long getUid(String key) {
        return getInstance().connect().sync().incr(key);
    }

    public <T> T eval(String lua, String[] keys, String[] vals, ScriptOutputType type) {
        try {
            RedisAdvancedClusterCommands<String, String> commands = getConnect().sync();
            return commands.eval(lua, type, keys, vals);
        } catch (Exception e) {
            log.error(">>>>>>>>> redis eval error === {}", e);
        }
        return null;
    }

    public String getStringValue(String key) {
        try {
            RedisAdvancedClusterCommands<String, String> commands = getConnect().sync();
            return commands.get(key);
        } catch (Exception e) {
            log.error(">>>>>>>>> redis getStringValue error === {}", e);
        }
        return null;
    }

    public <T> T getObjectValue(String key, Class<T> clazz) {
        try {
            RedisAdvancedClusterCommands<String, String> commands = getConnect().sync();
            String result = commands.get(key);
            return JSONObject.parseObject(result, clazz);
        } catch (Exception e) {
            log.error(">>>>>>>>> redis getObjectValue error === {}", e);
        }
        return null;
    }

    public boolean del(String key) {
        try {
            RedisAdvancedClusterCommands<String, String> commands = getConnect().sync();
            long result = commands.del(key);
            log.info(">>>>>>>>>>del result==={}", result);
            if (result > 0) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis del error === {}", e);
        }
        return false;
    }

    /**
     * 判断key是否存在
     *
     * @param key
     * @return
     */
    public boolean exists(String key) {
        try {
            RedisAdvancedClusterCommands<String, String> commands = getConnect().sync();
            long result = commands.exists(key);
            if (result > 0) {
                return true;
            }
        } catch (Exception e) {
            log.error(">>>>>>>>> redis exists error === {}", e);
        }
        return false;
    }

    /**
     * 重置存活时间
     *
     * @param key
     * @param seconds
     * @return
     */
    public boolean expire(String key, int seconds) {
        try {
            RedisAdvancedClusterCommands<String, String> commands = getConnect().sync();
            return commands.expire(key, seconds);
        } catch (Exception e) {
            log.error(">>>>>>>>> redis expire error === {}", e);
        }
        return false;
    }

    /**
     * 设置存活到期时间
     *
     * @param key
     * @param unixTime 到期时间的时间戳
     * @return
     */
    public boolean expireAt(String key, long unixTime) {
        try {
            RedisAdvancedClusterCommands<String, String> commands = getConnect().sync();
            return commands.expireat(key, unixTime);
        } catch (Exception e) {
            log.error(">>>>>>>>> redis expireAt error === {}", e);
        }
        return false;
    }

    @Override
    public void destroy() throws Exception {
        close(connect);
    }

}
